<!doctype html>

<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
  <script src="../../web-component-tester/browser.js"></script>
  <script src="../../iron-test-helpers/mock-interactions.js"></script>
  <link rel="import" href="../../test-fixture/test-fixture.html">

  <link rel="import" href="helpers.html">
  <link rel="import" href="../vaadin-grid.html">
  <link rel="import" href="../vaadin-grid-sorter.html">
  <link rel="import" href="../vaadin-grid-sort-column.html">
</head>

<body>

  <test-fixture id="sorter">
    <template>
      <vaadin-grid-sorter path="path">
        <span class="title">title</span><button>Button</button>
      </vaadin-grid-sorter>
    </template>
  </test-fixture>

  <test-fixture id="sorter-in-cell-content">
    <template>
      <vaadin-grid-cell-content>
        <vaadin-grid-sorter path="path"></vaadin-grid-sorter>
      </vaadin-grid-cell-content>
    </template>
  </test-fixture>

  <test-fixture id="grid">
    <template>
      <vaadin-grid style="width: 200px; height: 200px;" multi-sort>
        <vaadin-grid-column>
          <template class="header">
            <vaadin-grid-sorter path="first" direction="asc">
              <span class="title">first</span>
            </vaadin-grid-sorter>
          </template>
          <template>[[item.first]]</template>
        </vaadin-grid-column>
        <vaadin-grid-column>
          <template class="header">
            <span class="title">last</span>
            <vaadin-grid-sorter path="last" direction="desc">
            </vaadin-grid-sorter>
          </template>
          <template>[[item.last]]</template>
        </vaadin-grid-column>
        <vaadin-grid-sort-column></vaadin-grid-sort-column>
      </vaadin-grid>
    </template>
  </test-fixture>

  <test-fixture id="multiple-sorters">
    <template>
      <vaadin-grid style="width: 200px; height: 200px;">
        <vaadin-grid-column>
          <template class="header">
            <vaadin-grid-sorter path="first" direction="asc">
              <span class="title">first</span>
            </vaadin-grid-sorter>
          </template>
          <template>[[item.first]]</template>
        </vaadin-grid-column>
        <vaadin-grid-column>
          <template class="header">
            <span class="title">last</span>
            <vaadin-grid-sorter path="last">
            </vaadin-grid-sorter>
          </template>
          <template>[[item.last]]</template>
        </vaadin-grid-column>
      </vaadin-grid>
    </template>
  </test-fixture>

  <script>
    describe('sorting', () => {

      describe('sorter', () => {
        let sorter, title, button, indicators, orderIndicator;

        beforeEach(() => {

          sorter = fixture('sorter');
          title = sorter.querySelector('.title');
          button = sorter.querySelector('button');
          indicators = sorter.shadowRoot.querySelector('[part="indicators"]');
          orderIndicator = sorter.shadowRoot.querySelector('[part="order"]');
        });

        function clickSorter(sorter) {
          const clickEvent = new CustomEvent('click', {bubbles: true, cancelable: true, composed: true});
          sorter.dispatchEvent(clickEvent);
          return clickEvent;
        }

        it('should have default direction', () => {
          expect(sorter.direction).to.equal(null);
        });

        it('should toggle direction', () => {
          clickSorter(sorter);
          expect(sorter.direction).to.equal('asc');
          clickSorter(sorter);
          expect(sorter.direction).to.equal('desc');
          clickSorter(sorter);
          expect(sorter.direction).to.equal(null);
        });

        it('should not toggle on focusable click', () => {
          button.focus();
          click(button);
          expect(sorter.direction).to.equal(null);
        });

        it('should fire a sorter-changed event', done => {
          listenOnce(sorter, 'sorter-changed', () => done());
          sorter.direction = 'asc';
        });

        it('should show order indicator', () => {
          expect(orderIndicator.innerText).to.equal('');
          sorter._order = 0;
          expect(orderIndicator.innerText).to.equal('1');
          sorter._order = 4;
          expect(orderIndicator.innerText).to.equal('5');
        });

        it('should show direction indicator', () => {
          expect(sorter.getAttribute('direction')).to.equal(null);
          sorter.direction = 'asc';
          expect(sorter.getAttribute('direction')).to.equal('asc');
          sorter.direction = 'desc';
          expect(sorter.getAttribute('direction')).to.equal('desc');
        });

        it('should prevent default on click', () => {
          const clickEvent = clickSorter(sorter);

          expect(clickEvent.defaultPrevented).to.be.true;
        });
      });

      describe('grid', () => {
        let grid, sorterFirst, sorterLast;

        beforeEach(done => {

          grid = fixture('grid');
          flush(() => {
            sorterFirst = getHeaderCellContent(grid, 0, 0).querySelector('vaadin-grid-sorter');
            sorterLast = getHeaderCellContent(grid, 0, 1).querySelector('vaadin-grid-sorter');

            grid.items = [{
              first: 'foo',
              last: 'bar'
            }, {
              first: 'foo',
              last: 'baz'
            }, {
              first: 'bar',
              last: 'bar'
            }];

            flushGrid(grid);
            done();
          });
        });

        it('should be clickable', () => {
          const title = sorterFirst.querySelector('.title');
          expect(window.getComputedStyle(title).cursor).to.equal('pointer');
        });

        it('should ignore sorter', () => {
          sorterFirst.direction = '';
          const sortOrders = grid._mapSorters();
          expect(sortOrders).to.have.length(1);
          expect(sortOrders[0].path).to.equal('last');
          expect(sortOrders[0].direction).to.equal('desc');
        });

        it('should show order indicators', () => {
          expect(sorterFirst._order).to.equal(1);
          expect(sorterLast._order).to.equal(0);
        });

        it('should not show order indicators for one sorter', () => {
          sorterLast.direction = '';
          expect(sorterFirst._order).to.equal(null);
          expect(sorterLast._order).to.equal(null);
        });

        it('should not show order indicators', () => {
          sorterFirst.direction = null;
          sorterLast.direction = null;
          expect(sorterFirst._order).to.equal(null);
          expect(sorterLast._order).to.equal(null);
        });

        describe('array data provider', () => {

          it('should sort automatically', () => {
            expect(getBodyCellContent(grid, 0, 0).innerText).to.equal('foo');
            expect(getBodyCellContent(grid, 1, 0).innerText).to.equal('bar');
            expect(getBodyCellContent(grid, 2, 0).innerText).to.equal('foo');

            expect(getBodyCellContent(grid, 0, 1).innerText).to.equal('baz');
            expect(getBodyCellContent(grid, 1, 1).innerText).to.equal('bar');
            expect(getBodyCellContent(grid, 2, 1).innerText).to.equal('bar');
          });

          it('should sort automatically on sort', () => {
            sorterFirst.direction = null;
            expect(getBodyCellContent(grid, 0, 0).innerText).to.equal('foo');
            expect(getBodyCellContent(grid, 1, 0).innerText).to.equal('foo');
            expect(getBodyCellContent(grid, 2, 0).innerText).to.equal('bar');
          });

          it('should not sort the items if sorter directions are not defined', () => {
            sorterFirst.direction = null;
            sorterLast.direction = null;
            grid.items = buildDataSet(100);
            const bodyRows = getRows(grid.$.items);
            const cells = getRowCells(bodyRows[0]);
            expect(cells[0]._instance.item).to.equal(grid.items[0]);
          });

          it('should sort empty values', () => {
            grid.items = [{
              first: 'foo',
              last: 'bar'
            }, {
              first: '',
              last: ''
            }, {
              first: 'bar',
              last: 'bar'
            }];

            expect(getBodyCellContent(grid, 0, 0).innerText).to.equal('bar');
            expect(getBodyCellContent(grid, 1, 0).innerText).to.equal('foo');
            expect(getBodyCellContent(grid, 2, 0).innerText).to.equal('');
          });

          it('should sort null values', () => {
            grid.items = [{
              first: 'foo',
              last: 'bar'
            }, {
              first: null,
              last: null
            }, {
              first: 'bar',
              last: 'bar'
            }];

            expect(getBodyCellContent(grid, 0, 0).innerText).to.equal('bar');
            expect(getBodyCellContent(grid, 1, 0).innerText).to.equal('foo');
            expect(getBodyCellContent(grid, 2, 0).innerText).to.equal('');
          });

          it('should sort undefined values', () => {
            grid.items = [{
              first: 'foo',
              last: 'bar'
            }, {
              first: undefined,
              last: undefined
            }, {
              first: 'bar',
              last: 'bar'
            }];

            expect(getBodyCellContent(grid, 0, 0).innerText).to.equal('bar');
            expect(getBodyCellContent(grid, 1, 0).innerText).to.equal('foo');
            expect(getBodyCellContent(grid, 2, 0).innerText).to.equal('');
          });

          it('should sort NaN values', () => {
            grid.items = [{
              first: 'foo',
              last: 'bar'
            }, {
              first: NaN,
              last: NaN
            }, {
              first: 'bar',
              last: 'bar'
            }];

            expect(getBodyCellContent(grid, 0, 0).innerText).to.equal('bar');
            expect(getBodyCellContent(grid, 1, 0).innerText).to.equal('foo');
            expect(getBodyCellContent(grid, 2, 0).innerText).to.equal('NaN');
          });

          it('should sort numbers correctly', () => {
            grid.items = [{
              first: 1
            }, {
              first: 2
            }, {
              first: 11
            }];

            expect(getBodyCellContent(grid, 0, 0).innerText).to.equal('1');
            expect(getBodyCellContent(grid, 1, 0).innerText).to.equal('2');
            expect(getBodyCellContent(grid, 2, 0).innerText).to.equal('11');
          });

          it('should sort dates correctly', () => {
            grid.items = [{
              first: 1,
              last: new Date(2000, 1, 2)
            }, {
              first: 2,
              last: new Date(2000, 1, 3)
            }, {
              first: 3,
              last: new Date(2000, 1, 1)
            }];

            sorterFirst.direction = '';
            sorterLast.direction = 'asc';

            expect(getBodyCellContent(grid, 0, 0).innerText).to.equal('3');
            expect(getBodyCellContent(grid, 1, 0).innerText).to.equal('1');
            expect(getBodyCellContent(grid, 2, 0).innerText).to.equal('2');
          });

        });

        describe('data provider', () => {

          beforeEach(() => {
            grid.dataProvider = sinon.spy(grid.dataProvider);
            grid.clearCache();
          });

          it('should request with default sorting', () => {
            const lastCall = grid.dataProvider.lastCall;
            const params = lastCall.args[0];
            expect(params.sortOrders).to.eql([{
              path: 'last',
              direction: 'desc'
            }, {
              path: 'first',
              direction: 'asc'
            }]);
          });

          it('should request new data on sort', () => {
            sorterFirst.direction = 'desc';
            const lastCall = grid.dataProvider.lastCall;
            const params = lastCall.args[0];
            expect(params.sortOrders).to.eql([{
              path: 'first',
              direction: 'desc'
            }, {
              path: 'last',
              direction: 'desc'
            }]);
          });

          it('should request new data on change in existing sorters', () => {
            grid.dataProvider.reset();
            sorterLast.direction = 'asc';
            expect(grid.dataProvider.called).to.be.true;
          });

        });

        describe('single column sorting', () => {
          beforeEach(() => {
            grid.multiSort = false;
            grid.dataProvider = sinon.spy(grid.dataProvider);
          });

          it('should only using single sorter', () => {
            grid.dataProvider.reset();
            sorterFirst.direction = 'desc';

            expect(grid.dataProvider.args[1][0].sortOrders.length).to.eql(1);
          });

          it('should remove order from sorters', () => {
            // initial order before multiSort was set
            expect(sorterLast._order).to.eql(0);
            expect(sorterFirst._order).to.eql(1);

            sorterFirst.direction = 'desc';

            expect(sorterFirst._order).to.be.null;
            expect(sorterLast._order).to.be.null;
          });

          it('should remove direction from previous sorter', () => {
            sorterFirst.direction = 'desc';

            sorterLast.direction = 'desc';

            expect(sorterFirst.direction).to.be.null;
            expect(sorterLast.direction).to.eql('desc');
          });

          it('should set direction to also other than last sorter', () => {
            const grid = fixture('multiple-sorters');
            flushGrid(grid);
            const sorterFirst = getHeaderCellContent(grid, 0, 0).querySelector('vaadin-grid-sorter');
            expect(sorterFirst.direction).to.eql('asc');
          });
        });

        describe('accessibility', () => {
          function getSorterCell(sorter) {
            return sorter.parentNode.assignedSlot.parentNode;
          }

          it('should set aria-sort on cells', () => {
            expect(getSorterCell(sorterFirst).getAttribute('aria-sort')).to.equal('ascending');
            expect(getSorterCell(sorterLast).getAttribute('aria-sort')).to.equal('descending');
          });

          it('should update aria-sort on sorter change', () => {
            sorterFirst.direction = 'desc';
            sorterLast.direction = null;

            expect(getSorterCell(sorterFirst).getAttribute('aria-sort')).to.equal('descending');
            expect(getSorterCell(sorterLast).getAttribute('aria-sort')).to.equal('none');
          });
        });

        describe('sort-column', () => {
          let sortColumn, sorter;

          beforeEach(() => {
            sortColumn = grid.querySelector('vaadin-grid-sort-column');
            sorter = getHeaderCellContent(grid, 0, 2).querySelector('vaadin-grid-sorter');
          });

          it('should propagate path property to the internal vaadin-grid-sorter', () => {
            sortColumn.path = 'last';
            expect(sorter.path).to.equal('last');
          });

          it('should propagate direction property to the internal vaadin-grid-sorter', () => {
            sortColumn.direction = 'asc';
            expect(sorter.direction).to.equal('asc');
          });

          it('should notify direction property change from the internal vaadin-grid-sorter', done => {
            listenOnce(sortColumn, 'direction-changed', e => {
              expect(e.detail.value).to.equal('desc');
              done();
            });
            sorter.direction = 'desc';
          });

          it('should use header property to determine the text that gets slotted inside the sorter', () => {
            sortColumn.header = 'Last column';
            expect(sorter.textContent).to.equal('Last column');
          });

          it('should generate the text content based on path property, if header is not defined', () => {
            sortColumn.path = 'last';
            expect(sorter.textContent).to.equal('Last');
          });
        });
      });

    });
  </script>

</body>

</html>
